package com.autonavi.yunda.yunji.common.utils.geo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.autonavi.yunda.yunji.core.engine.exception.EngineException;
import org.springframework.stereotype.Component;

/**;
 * @author: liuchunyang;
 * @create: 2022-06-16 21:24;
 * @program: idc-di-uolap;
 * @description) wkt切块函数，返回值包含块与wkt的交集比例
 **/
;

@Component
public class Wkt2BlockFun {
    @Resource
    private WKTArea wktArea;

    public static void main(String[] args) throws Exception {
        Wkt2BlockFun t = new Wkt2BlockFun();
        t.wktArea = new WKTArea();
        String id = "A1";
        String wkt
            = "116.424683 39.909334,116.41404 39.8988,116.425369 39.894322,116.441506 39.90196,116.424683 39.909334";
        int scale = 100;
        Map<String, BlockInfo> process = t.process(id, wkt, scale);
        for (Map.Entry<String, BlockInfo> entry : process.entrySet()) {
            BlockInfo value = entry.getValue();
            System.out.println(value.toString());
        }
    }

    public Map<String, BlockInfo> process(String id, String wkt, int scale) throws Exception {

        //用于判断块与wkt相交关系的变量
        List<String> geoIdList = GeoBlockMgr.CalcFixedBlocks(GeoLatLon.wktTansGeoLatLon(wkt), scale);
        Set<String> geoIdSet = geoIdList.stream().collect(Collectors.toSet());
        double area = wktArea.areaOfWKTByBlock(geoIdList);
        HashSet<String> margin = new HashSet<>();
        double margin_coef = wktArea.calcCoefByArea(geoIdList, area, margin);

        Map<String, BlockInfo> result = new HashMap<>();
        try {
            wkt = wkt.replace("MULTIPOLYGON", "").replace("(((", "").replace(")))", "").trim();
            String pointList = wkt;

            if (scale != 1 && scale != 10 && scale != 50 && scale != 100 && scale != 500 && scale != 1000) {
                scale = 1000;
            }

            String envelope = GetEnvelope(pointList);
            String[] envelopeList = envelope.split(",");
            float xmin = Float.parseFloat(envelopeList[0].split(" ")[0]);
            float ymin = Float.parseFloat(envelopeList[0].split(" ")[1]);
            float xmax = Float.parseFloat(envelopeList[1].split(" ")[0]);
            float ymax = Float.parseFloat(envelopeList[1].split(" ")[1]);

            // blocktype 0 在多边形外部 1 相交 2在内部;
            int blocktype = 0;

            // 定义图幅编号的经纬度起点;
            float startlon = 73;
            float startlat = 17;
            float earchWDZC = (float)40008.08 * 1000;
            //这里是地球上1m的维度跨度;
            float wd_1unit = (float)0.000009000900090009001;
            int wdCount = (int)((ymax - ymin) / (wd_1unit * scale)) + 2;

            float _y = ymin;
            for (int i = 0; i < wdCount; i++) {
                //_y = ymin+i*wd_1unit*scale;
                int block_index_y = (int)(Math.floor((_y - startlat) / 2));
                //纬度 编号;
                int wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)) * scale);
                float wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit;

                if (scale == 1) {
                    wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)) * 10);
                    wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit / 10;
                }
                if (scale == 10) {
                    wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)) * scale);
                    wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit;
                }
                if (scale == 50) {
                    wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)) * 5);
                    wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit * 10;
                }
                if (scale == 100) {
                    wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)));
                    wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit * 100;
                }
                if (scale == 500) {
                    wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)) * 5);
                    wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit * 100;
                }
                if (scale == 1000) {
                    wd_index = ((int)((_y - startlat - block_index_y * 2) / (wd_1unit * scale)));
                    wd_min = block_index_y * 2 + startlat + wd_index * wd_1unit * 1000;
                }

                // 计算所在纬度的地球周长;
                float earchlength = (float)(earchWDZC * Math.cos(
                    (2 * Math.PI / 360) * (block_index_y * 2 + startlat + 1)));
                // 这里是经度1m 的 距离;
                float jd_1unit = 360 / earchlength;
                // jd_5m=jd_1unit*5;
                int jdCount = (int)((xmax - xmin) / (jd_1unit * scale)) + 2;

                //对下一次计算的坐标初始坐标进行确定;
                float wd_max = wd_min + wd_1unit * scale;
                //下一次的间隔;
                float wd_jiange_next = wd_1unit * scale;
                if (wd_max > (block_index_y + 1) * 2 + startlat) {
                    wd_max = (block_index_y + 1) * 2 + startlat;
                } else {
                    if (wd_max + wd_jiange_next > (block_index_y + 1) * 2 + startlat) {
                        wd_jiange_next = (block_index_y + 1) * 2 + startlat - wd_max;
                    }
                }
                _y = wd_max + wd_jiange_next / 2;

                //开始经度方向的循环;
                float _x = xmin;
                for (int j = 0; j < jdCount; j++) {
                    //_x = xmin+j*jd_1unit*scale;
                    int block_index_x = (int)(Math.floor((_x - startlon) / 2));
                    //经度编号 这里需要修改;
                    int jd_index = ((int)((_x - startlon - block_index_x * 2) / (jd_1unit * scale)) * scale);
                    float jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit;
                    if (scale == 1) {
                        jd_index = ((int)((_x - startlon - block_index_x * 2) / (jd_1unit * scale)) * 10);
                        jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit / 10;
                    }
                    if (scale == 10) {
                        jd_index = ((int)((_x - startlon - block_index_x * 2) / (jd_1unit * scale)) * scale);
                        jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit;
                    }
                    if (scale == 50) {
                        jd_index = ((int)((_x - startlon - block_index_x * 2) / (jd_1unit * scale)) * 5);
                        jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit * 10;
                    }
                    if (scale == 100) {
                        jd_index = (int)(((_x - startlon - block_index_x * 2) / (jd_1unit * scale)));
                        jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit * 100;
                    }
                    if (scale == 500) {
                        jd_index = ((int)((_x - startlon - block_index_x * 2) / (jd_1unit * scale)) * 5);
                        jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit * 100;
                    }
                    if (scale == 1000) {
                        jd_index = (int)(((_x - startlon - block_index_x * 2) / (jd_1unit * scale)));
                        jd_min = block_index_x * 2 + startlon + jd_index * jd_1unit * 1000;
                    }

                    if (block_index_x < -90) {
                        block_index_x = block_index_x + 180;
                    }

                    String jd_out_index = "0";
                    String wd_out_index = "0";
                    if (scale == 1) {
                        // 计算坐标全值;
                        {
                            jd_out_index = zfill(block_index_x, 2) + zfill(jd_index, 9);
                            wd_out_index = zfill(block_index_y, 2) + zfill(wd_index, 9);
                        }
                        if (block_index_x < 0) {
                            jd_out_index = zfill(block_index_x, 3) + zfill(jd_index, 9);
                        }
                        if (block_index_y < 0) {
                            wd_out_index = zfill(block_index_y, 3) + zfill(wd_index, 9);
                        }
                    }

                    if (scale == 10)
                    // 计算坐标全值;
                    {
                        jd_out_index = zfill(block_index_x, 2) + zfill(jd_index, 7);
                        wd_out_index = zfill(block_index_y, 2) + zfill(wd_index, 7);
                        if (block_index_x < 0) {
                            jd_out_index = zfill(block_index_x, 3) + zfill(jd_index, 7);
                        }
                        if (block_index_y < 0) {
                            wd_out_index = zfill(block_index_y, 3) + zfill(wd_index, 7);
                        }
                    }
                    if (scale == 50)
                    // 计算坐标全值;
                    {
                        jd_out_index = zfill(block_index_x, 2) + zfill(jd_index, 6);
                        wd_out_index = zfill(block_index_y, 2) + zfill(wd_index, 6);
                        if (block_index_x < 0) {
                            jd_out_index = zfill(block_index_x, 3) + zfill(jd_index, 6);
                        }
                        if (block_index_y < 0) {
                            wd_out_index = zfill(block_index_y, 3) + zfill(wd_index, 6);
                        }
                    }
                    if (scale == 100)
                    // 计算坐标全值;
                    {
                        jd_out_index = zfill(block_index_x, 2) + zfill(jd_index, 5);
                        wd_out_index = zfill(block_index_y, 2) + zfill(wd_index, 5);
                        if (block_index_x < 0) {
                            jd_out_index = zfill(block_index_x, 3) + zfill(jd_index, 5);
                        }
                        if (block_index_y < 0) {
                            wd_out_index = zfill(block_index_y, 3) + zfill(wd_index, 5);
                        }
                    }
                    if (scale == 500)
                    // 计算坐标全值;
                    {
                        jd_out_index = zfill(block_index_x, 2) + zfill(jd_index, 4);
                        wd_out_index = zfill(block_index_y, 2) + zfill(wd_index, 4);
                        if (block_index_x < 0) {
                            jd_out_index = zfill(block_index_x, 3) + zfill(jd_index, 4);
                        }
                        if (block_index_y < 0) {
                            wd_out_index = zfill(block_index_y, 3) + zfill(wd_index, 4);
                        }
                    }
                    if (scale == 1000)
                    // 计算坐标全值;
                    {
                        jd_out_index = zfill(block_index_x, 2) + zfill(jd_index, 3);
                        wd_out_index = zfill(block_index_y, 2) + zfill(wd_index, 3);
                        if (block_index_x < 0) {
                            jd_out_index = zfill(block_index_x, 3) + zfill(jd_index, 3);
                        }
                        if (block_index_y < 0) {
                            wd_out_index = zfill(block_index_y, 3) + zfill(wd_index, 3);
                        }
                    }

                    //对下一次计算的坐标初始坐标进行确定;
                    float jd_max = jd_min + jd_1unit * scale;
                    //下一次的间隔;
                    float jd_jiange_next = jd_1unit * scale;
                    if (jd_max > (block_index_x + 1) * 2 + startlon) {
                        jd_max = (block_index_x + 1) * 2 + startlon;
                    } else {
                        if (jd_max + jd_jiange_next > (block_index_x + 1) * 2 + startlon) {
                            jd_jiange_next = (block_index_x + 1) * 2 + startlon - jd_max;
                        }
                    }

                    _x = jd_max + jd_jiange_next / 2;

                    String cubelist = str(jd_min) + ' ' + str(wd_min) + ',';
                    cubelist = cubelist + str(jd_min) + ' ' + str(wd_max) + ',';
                    cubelist = cubelist + str(jd_max) + ' ' + str(wd_max) + ',';
                    cubelist = cubelist + str(jd_max) + ' ' + str(wd_min);

                    String geoId = jd_out_index + '_' + wd_out_index;
                    // 对全部都在区域里面的区块进行处理;
                    //if (scale != 1000 && IsPtInPoly(jd_min, wd_min, wkt) && IsPtInPoly(jd_min, wd_max, wkt) &&
                    // IsPtInPoly(jd_max, wd_min, wkt) && IsPtInPoly(jd_max, wd_max, wkt)) {
                    //使用更高效的方式判块与wkt的相交关系
                    if (geoIdSet.contains(geoId) && !margin.contains(geoId)) {
                        //float mj_out = PointsArea(cubelist);
                        float mj_out = scale * scale;
                        String geo_pos = str(jd_min) + ' ' + str(wd_min) + ',';
                        geo_pos = geo_pos + str(jd_max) + ' ' + str(wd_max);
                        List<String> tmpList = Arrays.asList(cubelist.split(","));
                        List<String> wktoutarray = new ArrayList<>();
                        wktoutarray.addAll(tmpList);
                        int pointCountwkt = len(wktoutarray);
                        wktoutarray.add(wktoutarray.get(0));
                        String newPyg = String.join(",", wktoutarray);
                        forward(geoId, geo_pos, str(mj_out), 1, newPyg, id, str(scale), result);
                        continue;
                    }
                    // 对部分在区域里面的区块进行处理;
                    //if self.IsPtInPoly(jd_min,wd_min,wkt) or self.IsPtInPoly(jd_min,wd_max,wkt) or self.IsPtInPoly
                    // (jd_max,wd_min,wkt) or self.IsPtInPoly(jd_max,wd_max,wkt))
                    String newPyg = CutDuobianxing(cubelist, wkt);
                    if (!newPyg.isEmpty()) {
                        float mj_out = PointsArea(newPyg);
                        //20170913对这里进行了修改，主要是为了过滤有效面积超小的区块，这些对使用没有意义;
                        if (mj_out > 0.001) {
                            float mj_cubelist = PointsArea(cubelist);
                            String geo_pos = str(jd_min) + ' ' + str(wd_min) + ',';
                            geo_pos = geo_pos + str(jd_max) + ' ' + str(wd_max);
                            List<String> tmpList = Arrays.asList(cubelist.split(","));
                            List<String> wktoutarray = new ArrayList<>();
                            wktoutarray.addAll(tmpList);
                            int pointCountwkt = len(wktoutarray);
                            wktoutarray.add(wktoutarray.get(0));
                            newPyg = String.join(",", wktoutarray);
                            forward(geoId, geo_pos, str(mj_out), mj_out / mj_cubelist, newPyg, id, str(scale), result);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new EngineException("1103", "wkt切块运行失败!");
        }
        return result;
    }

    private static String zfill(Object obj, int length) {
        String str = String.valueOf(obj);
        int strLen = str.length();
        if (strLen >= length) {
            return str.substring(strLen - length, strLen);
        } else {
            StringBuilder zero = new StringBuilder();
            for (int i = 0; i < length - strLen; i++) {
                zero.append("0");
            }
            return zero + str;
        }
    }

    private static void forward(String geoId, String geo_pos, String mj_out, float proportion, String newPyg, String id,
        String scale, Map<String, BlockInfo> result) {
        BlockInfo blockInfo = new BlockInfo();
        blockInfo.setGeoId(geoId);
        blockInfo.setGeoPos(geo_pos);
        blockInfo.setMjOut(mj_out);
        blockInfo.setProportion(proportion);
        blockInfo.setNewPyg(newPyg);
        blockInfo.setId(id);
        blockInfo.setScale(scale);

        result.put(geoId, blockInfo);
    }

    // 获取外包函数 格式 xmin ymin,xmax ymax;
    private static String GetEnvelope(String pointListStr) {
       /*'' ';
           :param pointList:list[(lon, lat)...]多边形点的顺序需根据顺时针或逆时针，不能乱;
       '' ';*/
        float xmin = 180;
        float ymin = 90;
        float xmax = 0;
        float ymax = 0;
        String[] pointList = pointListStr.split(",");
        int iCount = len(pointList);
        for (int i = 0; i < iCount; i++) {
            float px = Float.parseFloat(pointList[i].split(" ")[0]);
            float py = Float.parseFloat(pointList[i].split(" ")[1]);

            if (px < xmin) {
                xmin = px;
            }
            if (px > xmax) {
                xmax = px;
            }
            if (py < ymin) {
                ymin = py;
            }
            if (py > ymax) {
                ymax = py;
            }
        }
        return str(xmin) + ' ' + str(ymin) + ',' + str(xmax) + ' ' + str(ymax);
    }

    // 判断 点x y 在向量 x1 y1 x2 y2 的左侧还是右侧，大于0位左侧 小于0位右侧;
    private static float IsInside(float x1, float y1, float x2, float y2, float x, float y) {

        return (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1);
    }

    ;

    //获取两条线的交点位置;
    private static String GetJiaoDian(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
        float x, k2, b2, y, k1, b1;
        if (x1 == x2) {
            x = x1;
            k2 = (y4 - y3) / (x4 - x3);
            b2 = y3 - k2 * x3;
            y = k2 * x + b2;
            return str(x) + ' ' + str(y);
        }

        if (x3 == x4) {
            x = x3;
            k1 = (y2 - y1) / (x2 - x1);
            b1 = y1 - k1 * x1;
            y = k1 * x + b1;
            return str(x) + ' ' + str(y);
        }
        k1 = (y2 - y1) / (x2 - x1);
        k2 = (y4 - y3) / (x4 - x3);
        b1 = y1 - k1 * x1;
        b2 = y3 - k2 * x3;
        if (k1 == k2) {
            x = 0;
            y = 0;
        } else {
            x = (b2 - b1) / (k1 - k2);
            y = k1 * x + b1;
        }
        return str(x) + ' ' + str(y);
    }

    //多边形的裁切;
    private static String CutDuobianxing(String cubeList, String pointList) {
        // cubeList='1 1,1 5,5 5,5 1';
        // pointList='2 0,2 2,6 2,6 0,2 0';;
        List<String> cubeArray = Arrays.asList(cubeList.split(","));
        List<String> pointArray = Arrays.asList(pointList.split(","));

        List<String> temparray = new ArrayList();
        ;
        int k = -1;

        int cubeCount = cubeArray.size();
        int pointCount = pointArray.size();

        //删除wkt最后一个重复的节点;
        if (pointArray.get(0) == pointArray.get(pointCount - 1)) {
            pointArray.subList(0, pointCount - 2);
            pointCount = len(pointArray);
        }
        // 0 在外侧 1在内测;
        int flag = 0;
        if (cubeCount < 3) {
            return "";
        }
        for (int i = 0; i < cubeCount; i++) {
            // 裁剪框的;
            //print(i);
            float xstart = Float.parseFloat(cubeArray.get(i).split(" ")[0]);
            float ystart = Float.parseFloat(cubeArray.get(i).split(" ")[1]);

            float xend = 0;
            float yend = 0;
            if (i == cubeCount - 1) {
                xend = Float.parseFloat(cubeArray.get(0).split(" ")[0]);
                yend = Float.parseFloat(cubeArray.get(0).split(" ")[1]);
            } else {
                xend = Float.parseFloat(cubeArray.get(i + 1).split(" ")[0]);
                yend = Float.parseFloat(cubeArray.get(i + 1).split(" ")[1]);
            }

            float pointStartX = Float.parseFloat(pointArray.get(pointCount - 1).split(" ")[0]);
            float pointStartY = Float.parseFloat(pointArray.get(pointCount - 1).split(" ")[1]);

            temparray = new ArrayList();

            float isin = IsInside(xstart, ystart, xend, yend, pointStartX, pointStartY);

            if (isin > 0) {
                flag = 0;
            } else {
                flag = 1;
            }

            for (int j = 0; j < pointCount; j++) {
                float pointEndX = Float.parseFloat(pointArray.get(j).split(" ")[0]);
                float pointEndY = Float.parseFloat(pointArray.get(j).split(" ")[1]);

                isin = IsInside(xstart, ystart, xend, yend, pointEndX, pointEndY);
                if (isin <= 0) {
                    // 第j个点在内测;
                    if (flag == 0) {
                        //前一个点在外侧;
                        flag = 1;
                        // k=k+1;
                        //求交点，把交点存进结果的新多边形临时数组;
                        String jiaodian = GetJiaoDian(xstart, ystart, xend, yend, pointStartX, pointStartY, pointEndX,
                            pointEndY);
                        temparray.add(jiaodian);
                    }
                    temparray.add(str(pointEndX) + ' ' + str(pointEndY));
                }
                //当前点在外侧;
                else {
                    //前一个点在内侧;
                    if (flag == 1) {
                        flag = 0;
                        String jiaodian = GetJiaoDian(xstart, ystart, xend, yend, pointStartX, pointStartY, pointEndX,
                            pointEndY);
                        temparray.add(jiaodian);
                    }
                }
                // 把这次的终点当成下次的起点;
                pointStartX = pointEndX;
                pointStartY = pointEndY;
            }
            pointArray = temparray;
            pointCount = len(pointArray);
            if (pointCount < 1) {
                return "";
            }

        }
        return String.join(",", temparray);
    }

    //计算面积;
    private static float PointsArea(String pointList) {
        float earchWDZC = (float)40008.08 * 1000;
        String[] pointArray = pointList.split(",");
        int point_num = pointArray.length;
        if (point_num < 3) {
            return 0;
        }
        float wd = Float.parseFloat(pointArray[0].split(" ")[1]);
        float s = 0;
        s = (Float.parseFloat(pointArray[0].split(" ")[1])) * ((Float.parseFloat(
            pointArray[point_num - 1].split(" ")[0])) - (Float.parseFloat(pointArray[1].split(" ")[0])));
        for (int i = 1; i < point_num; i++) {

            s = s + (Float.parseFloat(pointArray[i].split(" ")[1])) * (Float.parseFloat(pointArray[i - 1].split(" ")[0])
                - Float.parseFloat(pointArray[(i + 1) % point_num].split(" ")[0]));
        }
        return (float)((Math.abs(s) / 2) * Math.cos((2 * Math.PI / 360) * wd) * (earchWDZC / 360) * (earchWDZC / 360));

    }

    private static boolean IsPtInPoly(float aLon, float aLat, String pointListstr) {

       /*'' ';
           :param aLon:float 经度;
       :param aLat:float 纬度;
       :param pointList:list[(lon, lat)...]多边形点的顺序需根据顺时针或逆时针，不能乱;
       '' '*/

        //if(isinmap['dddd']==0))
        //   return true;
        String[] pointList = pointListstr.split(",");
        int iSum = 0;
        int iCount = pointList.length;
        if (iCount < 3) {
            return false;
        }
        for (int i = 0; i < iCount; i++) {
            float pLon1 = Float.parseFloat(pointList[i].split(" ")[0]);
            float pLat1 = Float.parseFloat(pointList[i].split(" ")[1]);

            float pLon2;
            float pLat2;
            if (i == iCount - 1) {
                pLon2 = Float.parseFloat(pointList[0].split(" ")[0]);

                pLat2 = Float.parseFloat(pointList[0].split(" ")[1]);
            } else {
                pLon2 = Float.parseFloat(pointList[i + 1].split(" ")[0]);
                pLat2 = Float.parseFloat(pointList[i + 1].split(" ")[1]);
            }
            if (((aLat >= pLat1) && (aLat < pLat2)) || ((aLat >= pLat2) && (aLat < pLat1))) {
                float pLon = 0;

                if (Math.abs(pLat1 - pLat2) > 0) {
                    pLon = pLon1 - ((pLon1 - pLon2) * (pLat1 - aLat)) / (pLat1 - pLat2);

                    if (pLon < aLon) {
                        iSum += 1;
                    }
                }
            }
        }
        if (iSum % 2 != 0) {
            return true;
        } else {
            return false;
        }
    }

    private static int len(String s[]) {
        return s.length;
    }

    private static int len(List s) {
        return s.size();
    }

    private static String str(Object o) {
        return o.toString();
    }

}
